library webviewx;

import 'dart:async';
import 'dart:io';

import 'package:flutter/material.dart';
import 'package:webview_flutter/platform_interface.dart';
import 'package:webview_flutter/webview_flutter.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:flutter_user_agentx/flutter_user_agent.dart';

export 'package:webview_flutter/webview_flutter.dart';

String? _userAgent;

class WebViewx extends StatefulWidget {
  final String? initialUrl;
  final String? appendUserAgent;
  final Widget? loadFailWidget;
  final Color progressBackground;
  final Color progressColor;
  final double progressHeight;
  final Set<JavascriptChannel>? javascriptChannels;
  final Function(WebViewController controller)? onWebViewCreated;
  final PageStartedCallback? onPageStarted;
  final PageFinishedCallback? onPageFinished;
  final Future<NavigationDecision> Function(NavigationRequest navigation)?
      navigationDelegate;
  WebViewx(
      {@required this.initialUrl,
      this.appendUserAgent,
      this.loadFailWidget,
      this.progressBackground = Colors.greenAccent,
      this.progressColor = Colors.red,
      this.progressHeight = 1.5,
      this.javascriptChannels,
      this.onWebViewCreated,
      this.onPageStarted,
      this.onPageFinished,
      this.navigationDelegate});

  @override
  State<StatefulWidget> createState() => WebViewxState();
}

class WebViewxState extends State<WebViewx> {
  WebViewController? controller;
  String? loadingUrl;
  String? userAgent = _userAgent;
  StreamController<WebResourceError?>? loadFailController;
  StreamController<double>? progressController;
  @override
  void initState() {
    super.initState();
    initUserAgent();

    if (Platform.isAndroid) {
      WebView.platform = SurfaceAndroidWebView();
    }
    loadFailController = StreamController<WebResourceError>();
    progressController = StreamController<double>();
  }

  @override
  void dispose() {
    loadFailController?.close();
    progressController?.close();
    super.dispose();
  }

  void initUserAgent() async {
    if (userAgent == null) {
      await FlutterUserAgent.init();
      if (widget.appendUserAgent != null && userAgent == null) {
        String? ua = FlutterUserAgent.webViewUserAgent;
        setState(() {
          userAgent = "$ua ${widget.appendUserAgent}";
          _userAgent = userAgent;
        });
      }
    }
  }

  @override
  Widget build(BuildContext context) {
    if (widget.appendUserAgent != null && userAgent == null) {
      return SizedBox();
    }
    return Stack(
      children: [
        WebView(
          initialUrl: widget.initialUrl,
          javascriptMode: JavascriptMode.unrestricted,
          userAgent: userAgent,
          javascriptChannels: widget.javascriptChannels,
          initialMediaPlaybackPolicy: AutoMediaPlaybackPolicy.always_allow,
          onWebViewCreated: (WebViewController c) {
            controller = c;
            if (widget.onWebViewCreated != null) {
              widget.onWebViewCreated!(c);
            }
          },
          navigationDelegate: (NavigationRequest navigation) async {
            if (widget.navigationDelegate != null) {
              var decision = await widget.navigationDelegate!(navigation);
              if (decision == NavigationDecision.prevent) {
                return NavigationDecision.prevent;
              }
            }
            String url = navigation.url;
            bool isHttp = url.substring(0, 4) == 'http';
            if (!isHttp) {
              bool installed = await canLaunch(url);
              if (installed) {
                launch(url);
              }
              return NavigationDecision.prevent;
            }

            reload();
            loadingUrl = url;
            return NavigationDecision.navigate;
          },
          onPageStarted: (url) {
            reload();
            loadingUrl = url;
            if (widget.onPageStarted != null) {
              widget.onPageStarted!(url);
            }
          },
          onProgress: (progress) {
            progressController?.add(progress / 100);
          },
          onPageFinished: widget.onPageFinished,
          onWebResourceError: (WebResourceError error) async {
            String? failingUrl = error.failingUrl;
            if (failingUrl == loadingUrl) {
              loadFail(error);
            }
          },
        ),
        _FailMask(
          loadFail: loadFailController,
          loadFailWidget: widget.loadFailWidget,
        ),
        _ProgressIndicator(
          progress: progressController,
          progressHeight: widget.progressHeight,
          progressColor: widget.progressColor,
          progressBackground: widget.progressBackground,
        ),
      ],
    );
  }

  void loadFail(WebResourceError error) {
    loadFailController?.add(error);
  }

  void reload() {
    loadFailController?.add(null);
  }

  void loadSuccess() {
    loadFailController?.add(null);
  }
}

class _ProgressIndicator extends StatefulWidget {
  final Color? progressBackground;
  final Color? progressColor;
  final double? progressHeight;
  final StreamController<double>? progress;

  _ProgressIndicator(
      {this.progress,
      this.progressBackground,
      this.progressColor,
      this.progressHeight});

  @override
  State<_ProgressIndicator> createState() => _ProgressIndicatorState();
}

class _ProgressIndicatorState extends State<_ProgressIndicator> {
  @override
  Widget build(BuildContext context) {
    Color? backgroundColor = widget.progressBackground;
    Color? valueColor = widget.progressColor;
    double? minHeight = widget.progressHeight;
    return StreamBuilder<double>(
        stream: widget.progress?.stream,
        builder: (context, snapshot) {
          double value = 0;
          if (snapshot.hasData) {
            value = snapshot.data!;
          }

          Widget widget = LinearProgressIndicator(
            minHeight: minHeight,
            value: value,
            backgroundColor: backgroundColor,
            valueColor: AlwaysStoppedAnimation<Color>(valueColor!),
          );

          if (value >= 0.5) {
            widget = AnimatedOpacity(
              duration: Duration(milliseconds: 1000),
              opacity: value >= 1 ? 0 : 1,
              child: widget,
            );
          }
          return Positioned(top: 0, left: 0, right: 0, child: widget);
        });
  }
}

class _FailMask extends StatelessWidget {
  final Widget? loadFailWidget;
  final StreamController<WebResourceError?>? loadFail;

  _FailMask({this.loadFailWidget, this.loadFail});

  @override
  Widget build(BuildContext context) {
    return StreamBuilder<WebResourceError?>(
        stream: loadFail?.stream,
        builder: (context, snapshot) {
          if (!snapshot.hasData || snapshot.data == null) {
            return SizedBox();
          }
          return Container(
            width: double.infinity,
            height: double.infinity,
            color: Colors.white,
            alignment: Alignment.center,
            child: loadFailWidget ??
                Text(
                  "${snapshot.data?.description}",
                  textAlign: TextAlign.center,
                ),
          );
        });
  }
}
